/*
 * 础光实时操作系统PhotonRTOS -- 中断实现
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 * 		Zicheng Hu <huzicheng@kernelsoft.com>
 * 		Yili Zhang <s-zhangyili@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <photon/irq.h>
#include <photon/sched.h>
#include <photon/errno.h>
#include <photon/printk.h>
#include <photon/process.h>
#include <photon/timex.h>

#include <autosar/internal.h>

/**
 * 二类中断全局标志：启用/禁用
 */
bool cat_2_interrupt = true;
/**
 * 中断数量，决定映射表大小
 */
uint32_t irq_count = 0;

/**
 * 设置中断类型，*有*锁保护
 */
int32_t set_irq_trigger_type(uint32_t hw_irq, uint32_t type)
{
	int32_t ret = 0;

	if (os_irq_controller.set_trigger_type != NULL)	{
		ret = os_irq_controller.set_trigger_type(hw_irq,	\
				type & HWIRQ_TRIGGER_TYPE_MASK );
	}

	return ret;
}

/**
 * 屏蔽/取消屏蔽中断
 */
void mask_irq(uint32_t hw_irq)
{
	if (os_irq_controller.mask != NULL) {
		os_irq_controller.mask(hw_irq);
	}
}

/**
 * 解除屏蔽/使能中断
 */
void unmask_irq(uint32_t hw_irq)
{
	if (os_irq_controller.unmask != NULL) {
		os_irq_controller.unmask(hw_irq);
	}
}

/**
 * 清除中断等待
 */
void clear_irq_pending(uint32_t hw_irq)
{
	if (os_irq_controller.clear_pending != NULL) {
		os_irq_controller.clear_pending(hw_irq);
	}
}

/**
 * 设置中断优先级
 */
int32_t set_irq_priority(uint32_t hw_irq, uint32_t pri)
{
	int32_t ret = 0;
	if (os_irq_controller.set_priority != NULL) {
		ret = os_irq_controller.set_priority(hw_irq, pri);
	}
	return ret;
}

/**
 * 得到中断优先级，范围最大到255，当前是16
 */
uint32_t get_irq_priority(uint32_t hw_irq)
{
	uint32_t ret = 256;
	if (os_irq_controller.get_priority != NULL) {
		ret = os_irq_controller.get_priority(hw_irq);
	}
	return ret;
}

uint32_t get_irq_status(uint32_t hw_irq)
{
	uint32_t ret = 0;
	if (os_irq_controller.get_status != NULL) {
		ret = os_irq_controller.get_status(hw_irq);
	}
	return ret;
}

/**
 * 屏蔽/取消屏蔽中断
 */
static inline void ack_irq(uint32_t hw_irq)
{
	if (os_irq_controller.ack != NULL) {
		os_irq_controller.ack(hw_irq);
	}
}

struct irq_desc *get_irq_desc(uint32_t hw_irq)
{
	int i;
	struct irq_desc *desc = NULL;

	for (i = 0; i < NR_IRQS; i++) {
		if (os_irq_desc[i] != NULL && os_irq_desc[i]->hw_irq == hw_irq) {
			desc = os_irq_desc[i];
			break;
		}
	}
	return desc;
}

/**
 * 中断序言
 */
static void irq_preface(uint32_t irq)
{
	add_preempt_count(HARDIRQ_OFFSET);
}

/**
 * 中断尾声
 */
static void irq_tail(uint32_t irq, void *regs)
{
	struct irq_desc *desc;

	disable_irq();
	desc = get_irq_desc(irq);
	/**
	 * 这里直接减计数，前面已经关中断了。
	 */
	sub_preempt_count(HARDIRQ_OFFSET);
	/**
	 * 处理抢占，二类中断才需要再次调度
	 */
	if (desc->isr2 && need_resched() != 0) {
		uintptr_t flags;
		local_irq_save(flags);
		preempt_in_irq();
		local_irq_restore(flags);
	}
}

uintptr_t irq_err_count;
int32_t do_hard_irq(uint32_t hw_irq, void *regs)
{
	struct irq_desc *desc;
	int32_t ret = 0;

#ifdef AUTOSAR_TIMING_PROTECTION
	uint64_t irq_start_time = cycles_to_usecs(get_cycles());
#endif
	current_proc_info()->current_irq = hw_irq;

	/**
	 * 处理抢占计数，rcu等等
	 */
	irq_preface(hw_irq);
	/*
	 * 错误的硬件号
	 */
	if (hw_irq >= NR_IRQS) {
		irq_err_count++;
		ret = -EINVAL;
	} else {
		/**
		 * 进行通常的中断处理
		 */
		desc = get_irq_desc(hw_irq);

		desc->occur_count++;

		if (desc->handler != NULL) {
			uint32 context = desc->isr2 ? ENV_CAT2_ISR : ENV_CAT1_ISR;
			CONTEXT_MARK_START(context)
			desc->handler();
			CONTEXT_MARK_END
		} else {
			mask_irq(hw_irq);
		}
		/**
		 * 应答中断
		 */
		ack_irq(hw_irq);
	}
#ifdef AUTOSAR_TIMING_PROTECTION
	if (time_protect_disable == 0 && current->is_autosar_task && current->
		cp_attr.cp_isr_execution_budget + irq_start_time < cycles_to_usecs(get_cycles())) {
		autosar_protect_hook(E_OS_PROTECTION_TIME);
		irq_start_time = cycles_to_usecs(get_cycles());
	}
#endif
	/**
	 * 处理中断尾声
	 */
	irq_tail(hw_irq, regs);
	/**
	 * [SWS_Os_00368] 如果2类中断调用了DisableAllInterupts() / SuspendAllInterrupts() / SuspendOSInterrupts()而没有调用相应的使能方法，
	 * 则应该在此处补充调用，并调用ErrorHook()。
	*/
	if (desc->isr2) {
		if (accurate_read(&osek_all_interrupt_nesting[GetCoreID()]) > 0 ||
			accurate_read(&osek_os_interrupt_nesting[GetCoreID()]) > 0) {

			while (accurate_read(&osek_all_interrupt_nesting[GetCoreID()]) > 0)
				ResumeAllInterrupts();

			while (accurate_read(&osek_os_interrupt_nesting[GetCoreID()]) > 0)
				ResumeOSInterrupts();

			ErrorHook(E_OS_DISABLEDINT);
		}
	}
#if defined(CONFIG_CPU_CORTEX_M)
	enable_irq();
#endif
	return ret;
}

static void do_close_irq(struct irq_desc *irqc)
{
	pr_err("generated interruption storm on irq %d\n", irqc->hw_irq);
	mask_irq(irqc->hw_irq);
}

void check_irq_storm(void)
{
	int i;
	struct irq_desc *irqc;

	for (i = 0; i < NR_IRQS; i++) {
		irqc = os_irq_desc[i];
		if (irqc == NULL) {
			continue;
		}
		if (irqc->occur_count > irqc->occur_threshold) {
			do_close_irq(irqc);
		}
		irqc->occur_count = 0;
	}
}
